import os
import cv2 as cv 
import pickle 
import numpy as np 
import matplotlib.pyplot as plt
from os.path import exists
from imutils import paths 
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split

'''得到图片，截取人脸部分保存至fase_data，将fase_data标准化，
    获得fase_data数据内容得到X,y。将数据进行拟合，使用测试集中的数据进行测试，
    输出图像，并在图像中标明人脸位置以及识别的标签值。'''

def progress_bar(i):
    print("\r", end="")  # 输出位置回到行首
    print('进度'+": {}%: ".format(i), "▋" * (i // 2), end="") 

def Get_fase():
    if exists('data/X') and exists('data/y'):   # 当存在X，y文件时直接读取
        print('已经读入图片，生成X,y')
        with open('data/X','rb') as f:
            X = pickle.load(f)
        with open('data/y','rb') as f:
            y = pickle.load(f)
        return X,y
    X = []
    y = []
    i = 0
    ### 生成X，y ###
    img_paths = list(paths.list_images('data/train'))
    for img_path in img_paths:
        
        img = cv.imread(img_path, 0)    # 以灰度图读取图片
        File = os.path.split(img_path)[-1] # 把path（路径）分成路径和文件
        label = File.split('.')[0]      # 获得y值，标签 
        faces = face_cascade.detectMultiScale(img)# 获得图像的人脸信息
        
        # faces列表中每一个元素包含四个值：面部左上角的坐标(x,y) 以及面部的宽和高(w,h)

        for (x_p,y_p,w,h) in faces:         # 图片中不止一个人脸列表中不止一个人脸信息
            progress_bar(i*100//len(img_paths))
            img_fase = img[y_p:y_p+h, x_p:x_p+w]     # 截取人脸
            img_size = cv.resize(img_fase, (50, 50))  # 将图像转换成指定长度

            pixel_list = img_size.reshape(-1)# 将图像拉直
            X.append(pixel_list)        # 将图像存入X中
            y.append(label)
            i=i+1
    print('x的形状', np.shape(X))
    with open("data/X", 'wb') as f:
        pickle.dump(X, f)
        print("已生成X, 结构:",np.shape(X))
    with open("data/y", 'wb') as f:
        pickle.dump(y, f)
        print("已生成y, 结构:",np.shape(y))
    return X,y

face_cascade = cv.CascadeClassifier() # 创建级联分类器                  
# 引入训练好的可用于人脸识别的级联分类器模型 
face_cascade.load("./haarcascade_frontalface_alt.xml") 

### 获得X，y ###
X, y = Get_fase()
print('已经获得X,y')
print("X结构:",np.shape(X))
print("y结构:",np.shape(y))

### 将X，y分割成训练集和测试集 ###
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2)
### 使用svc ###
print('开始使用svc拟合')
svc = SVC( )    # 建立模型并拟合数据
svc.fit(X_train, y_train)
acc = round(((svc.score(X_test,y_test))*100),2) #求出准确度并规范化（转换成百分之并保留两位）
print('拟合完成')
img_file = np.random.choice(list(paths.list_images("data/test")),1)[0] # 随机获得测试集中的图片
img=cv.imread(img_file)                         # 载入彩图，供最后显示使用
img_gray = cv.imread(img_file, 0)               # 以灰度图载入图片
faces = face_cascade.detectMultiScale(img_gray) # 找到人脸位置，并返回坐标
for (X_p, y_p, w, h) in faces:                  # 实现在一张图中可以识别多张人脸
    img_face=cv.resize(img_gray[y_p:y_p+h, X_p:X_p+w], (50, 50))    # 截取人脸位置图像，并统一大小
    img_ravel=img_face.ravel()                      # 拉成一维
    index=svc.predict([img_ravel])              # 获得标签
    cv.rectangle(img, (X_p, y_p), (X_p+w, y_p+h), (0, 255, 0), 2)   # 用绿框框出人脸的范围
    cv.putText(img, 
                ('acc='+str(acc)), # 要显示的内容
                (0,int(img.shape[0]*(1/15))), # 要显示的位置
                cv.FONT_HERSHEY_SIMPLEX, # 要使用的字体 -> 一般英文字体
                1, # 字体放大倍数
                (0,0,255), # 字体颜色
                2) 
    cv.putText(img, # 要显示字体的图片
                str(index), # 要显示的内容
                (X_p,y_p-10), # 要显示的位置
                cv.FONT_HERSHEY_SIMPLEX, # 要使用的字体 -> 一般英文字体
                1, # 字体放大倍数
                (0,255,0), # 字体颜色
                2)


### 显示图片
fg,ax=plt.subplots()
ax.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))   # 显示图片，并将bgr转换成rgb
plt.tight_layout()
plt.show()

